/**
 * 标准日志定义文件
 * 张勇 41204@qq.com
 */

#if (defined _MSC_VER && !defined _CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#ifdef _WIN32
#include <io.h>
#include <windows.h>
#else // linux, unix, vitis ?
//#include <unistd.h>
#endif

#include "utils.h"
#include "unios.h"
#include "log.h"
#include "rolling-file.h"
#include "transceiver.h"

// 查找第一个 writer 位置
static int _first_writer(logger_t* logger, void* writer) {
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] == writer) return i;
    }
    return -1;
}

void logger_set_level(logger_t* logger, int level) {
    if (!logger || level < LOG_EMERG || level > LOG_DEBUG) return;
    logger->level = level;
}

void logger_del_writer(logger_t* logger, log_writer_t *writer) {
    if (!logger || !writer) return;
    unios_critical_section_do(logger->cs, 1000, {
        int idx = _first_writer(logger, writer);
        if(idx < 0) break;
        
        if (writer->close != NULL) {
            writer->close(writer);   // 如果 writer 有 close 函数指针，则调用它关闭 writer
        } else {
            unios_free(writer);      // 否则直接释放 writer 内存              
        }
        logger->writers[idx] = NULL; // 清空 writer 登记表项
    });
}

void logger_flush(logger_t* logger) {
    if (!logger) return;
    unios_critical_section_enter(logger->cs, 1000);
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i]->flush != NULL) {
            logger->writers[i]->flush(logger->writers[i]);
        }
    }
    unios_critical_section_leave(logger->cs);
}

int logger_puts(struct _logger* logger, const char* msg) {
	if(!logger || !msg) return 0;
	size_t len = strlen(msg); if(len < 1) return 0;

    // 调用各个 writer 输出
    unios_critical_section_enter(logger->cs, 1000);
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] && logger->writers[i]->write != NULL) {
            logger->writers[i]->write(logger->writers[i], msg);
        }
    }
    unios_critical_section_leave(logger->cs);

    return (int)len;
}

int logger_putchar(struct _logger* logger, const char ch) {
    char buf[2] = {ch, '\0'};
    return logger_puts(logger, buf);
}

void logger_dumpbin(struct _logger* logger, const void *buf, size_t len, size_t cpl, bool show_offset, bool show_ascii) {
    if(!logger || !buf || !len) return;
    size_t l, i, pos;
    size_t lc = len/cpl + ((len%cpl) ? 1:0);   // lines count
    const unsigned char *data = (const unsigned char*)buf;
    unsigned char ch;

    // 逐行输出
    unios_critical_section_enter(logger->cs, 1000);
    for(l=0; l<lc; l++) {
        pos = l*cpl;

        // 行首打印偏移地址
        if(show_offset) {
            logger->puts(logger, to_hex(pos & 0xFFFF, 4));
            logger->puts(logger, ": ");
        }

        // 打印十六进制
        for(i=0; i<cpl; i++) {
            if(pos < len) {
                logger->puts(logger, to_hex(data[pos], 2));
                logger->puts(logger, " ");
            } else {
            	logger->puts(logger, "   ");
            }
            pos++;
        }

        pos = l*cpl;

        // 打印字符
        if(show_ascii) {
			for(i=0; i<cpl; i++) {
				ch = data[pos++];
				logger->putc(logger, ((ch < 0x20 || ch > 0x7F) ? '.' : ch));
				if(pos >= len) break;
			}
        }

        logger->puts(logger, "\r\n");
    }
    unios_critical_section_leave(logger->cs);
}

int logger_printf(logger_t* logger, const char* format, ...) {
    size_t size = 0;
    // 日志缓冲区
    char buf[LOG_BUFFER_SIZE];

    // 定义一个 va_list 类型的变量，用来储存单个参数
    va_list args;
    // 使args指向可变参数的第一个参数
    va_start(args, format);
    // 必须用 vprintf() 等带V的
    size = vsnprintf(buf + size, LOG_BUFFER_SIZE - size, format, args);
    // 结束可变参数的获取
    va_end(args);

    // 输出
    return logger_puts(logger, buf);
}

int logger_log(logger_t* logger, int level, const char* tag, const char* format, ...) {
    // 如果要输出的日志级别大于当前设置的级别，则不输出
    if (level > logger->level || !format) { return 0; }
    size_t size = 0;
    // 日志缓冲区
    char buf[LOG_BUFFER_SIZE];

    // 填写时间戳
    int year, month, day, hour, minute, second;
    unios_rtc_get(&year, &month, &day, &hour, &minute, &second, NULL);
    size += sprintf(buf + size, "%d-%02d-%02d %02d:%02d:%02d.%03d ", year, month, day, hour, minute, second, (int)(unios_ms_now() % 1000));

    // 填写 level
    if (level >= 0 && level <= LOG_DEBUG) {
        static const char* _lname[] = { "EMERG ", "ALERT ", " CRIT ", "ERROR ", " WARN ", " NOTE ", " INFO ", "DEBUG " };
        const char* lname = _lname[level];
        memcpy(buf + size, lname, strlen(lname)); size += strlen(lname);
    }

    // 填写 tag
    if (tag) {
        strcpy(buf + size, tag);   size += strlen(tag);
        strcpy(buf + size, " | "); size += 3;
    }

    // 定义一个 va_list 类型的变量，用来储存单个参数
    va_list args;
    // 使args指向可变参数的第一个参数
    va_start(args, format);
    // 必须用 vprintf() 等带V的
    size = vsnprintf(buf + size, LOG_BUFFER_SIZE - size, format, args);
    // 结束可变参数的获取
    va_end(args);

    // 输出
    return logger_puts(logger, buf);
}

log_writer_t* logger_add_writer(logger_t* logger, log_writer_t *writer) {
    if (!logger || !writer) return NULL;
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] == NULL) {
            logger->writers[i] = (log_writer_t*)writer;
            return (log_writer_t*)writer;
        }
    }
    return NULL;
}

// writefunc 型 writer 数据结构(注意前面几个字段要与 _log_writer_t 的相同)
typedef struct _log_writer_writefunc {
    void (*write)(log_writer_t* self, const char* msg);
    void (*flush)(log_writer_t* self);
    void (*close)(log_writer_t* self);
    void* cookie;
} log_writer_writefunc_t;

log_writer_t* logger_add_writefunc(logger_t* logger, log_writefunc_t writefunc, void* cookie) {
    if (!logger || !writefunc) return NULL;
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] == NULL) {
            log_writer_writefunc_t *writer = MALLOC0(log_writer_writefunc_t);
            if(!writer) return NULL;
            writer->write = writefunc;
            writer->flush = NULL;
            writer->close = NULL;
            writer->cookie = cookie;
            logger->writers[i] = (log_writer_t*)writer;
            return (log_writer_t*)writer;
        }
    }
    return NULL;
}


// transceiver 型 writer 数据结构(注意前面几个字段要与 _log_writer_t 的相同)
typedef struct _log_writer_transceiver {
    void (*write)(struct _log_writer_transceiver* self, const char* msg);
    void (*flush)(struct _log_writer_transceiver* self);
    void (*close)(struct _log_writer_transceiver* self);
    transceiver_t* trx;
} log_writer_transceiver_t;

static inline void _transceiver_write(log_writer_transceiver_t* self, const char* msg) {
	self->trx->tx(self->trx, (void*)msg, strlen(msg), NULL);
}

static inline void _transceiver_close(log_writer_transceiver_t* self) {
	self->trx->close(self->trx);
}

log_writer_t* logger_add_transceiver(logger_t* logger, transceiver_t* trx) {
    if (!logger || !trx) return NULL;
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] == NULL) {
            log_writer_transceiver_t *writer = MALLOC0(log_writer_transceiver_t);
            if(!writer) return NULL;
            writer->write = _transceiver_write;
            writer->flush = NULL;
            writer->close = _transceiver_close;
            writer->trx = trx;
            logger->writers[i] = (log_writer_t*)writer;
            return (log_writer_t*)writer;
        }
    }
    return NULL;
}


// file 型 writer 数据结构(注意前面几个字段要与 _log_writer_t 的相同)
typedef struct _log_writer_file {
    void (*write)(struct _log_writer_file* self, const char* msg);
    void (*flush)(struct _log_writer_file* self);
    void (*close)(struct _log_writer_file* self);
    FILE* fp;
} log_writer_file_t;

static inline void _file_write(log_writer_file_t* self, const char* msg) {
    fwrite(msg, 1, strlen(msg), self->fp);
}

static inline void _file_flush(log_writer_file_t* self) {
    fflush(self->fp);
}

static inline void _file_close(log_writer_file_t* self) {
    fclose(self->fp);
}

log_writer_t* logger_add_file(logger_t* logger, FILE* fp) {
    if (!logger || !fp) return NULL;
    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i] == NULL) {
            log_writer_file_t *writer = MALLOC0(log_writer_file_t);
            if(!writer) return NULL;
            writer->write = _file_write;
            writer->flush = _file_flush;
            writer->close = _file_close;
            writer->fp = fp;
            logger->writers[i] = (log_writer_t*)writer;
            return (log_writer_t*)writer;
        }
    }
    return NULL;
}


static inline void _stdout_write(log_writer_t* self, const char* msg) {
    fwrite(msg, 1, strlen(msg), stdout);
}

log_writer_t* logger_add_stdout(logger_t* logger) {
    if (!logger) return NULL;
    int empty_idx = -1;

    for (int i = 0; i < LOG_MAX_WRITER; i++) {
        if (logger->writers[i]) {
            // 如果已经添加过 stdout writer 就不用再添加了
            if(logger->writers[i]->write == _stdout_write) return logger->writers[i];
        } else {
            // 第一个空位
            if(empty_idx < 0) empty_idx = i;
        }
    }
    
    // 没有空位了，不能再添加 writer
    if(empty_idx < 0) return NULL;

    // 添加 stdout writer
    log_writer_t *writer = MALLOC0(log_writer_t);
    if(!writer) return NULL;
    writer->write = _stdout_write;
    writer->flush = NULL;
    writer->close = NULL;
    logger->writers[empty_idx] = (log_writer_t*)writer;
    return (log_writer_t*)writer;
}


// rolling_file 型 writer 数据结构(注意前面几个字段要与 _log_writer_t 的相同)
typedef struct _log_writer_rolling_file {
    void (*write)(struct _log_writer_rolling_file* self, const char* msg);
    void (*flush)(struct _log_writer_rolling_file* self);
    void (*close)(struct _log_writer_rolling_file* self);
    unios_file_t fd;
    int max_filesize;
    int max_filecount;
    char filepath[256];
} log_writer_rolling_file_t;

static void _rolling_file_write(log_writer_rolling_file_t* self, const char* msg) {
    // 刷新并取得 rolling file 句柄
    int err = get_rolling_file(&self->fd, self->filepath, self->max_filesize, self->max_filecount);
    if (err) return;

    // 输出日志到日志文件
    err = unios_file_write(self->fd, msg, (unsigned int)strlen(msg), 0, NULL);
}

static inline void _rolling_file_flush(log_writer_rolling_file_t* self) {
	unios_file_sync(self->fd);
}

static void _rolling_file_close(log_writer_rolling_file_t* self) {
    // 保存缓冲区数据
    _rolling_file_flush(self);
    // 关闭文件
    unios_file_close(self->fd);
}

log_writer_t* logger_add_rolling_file(logger_t* logger, const char* filepath, int max_filesize, int max_filecount) {
    if (!logger || !filepath || !max_filesize || !max_filecount) return NULL;

    // 查找一个 writer 空位置
    int idx = _first_writer(logger, NULL); if(idx < 0) return NULL;

    // 尝试打开日志文件
    unios_file_t fd = unios_file_null();
    std_err_t err = get_rolling_file(&fd, filepath, max_filesize, max_filecount);
    if (err || unios_file_is_valid(fd)) return NULL;

    // 分配日志参数存储空间
    log_writer_rolling_file_t* writer = MALLOC0(log_writer_rolling_file_t);
    if (!writer) return NULL;

    // 保存日志文件参数
    writer->fd = fd;
    writer->max_filesize = max_filesize;
    writer->max_filecount = max_filecount;
    strncpy(writer->filepath, filepath, sizeof(writer->filepath)-1);

    // 填写 writer 信息
    writer->write = _rolling_file_write;
    writer->flush = _rolling_file_flush;
    writer->close = _rolling_file_close;
    
    logger->writers[idx] = (log_writer_t*)writer;
    return (log_writer_t*)writer;
}


#ifdef _WIN32

// 参考: https://learn.microsoft.com/zh-cn/windows/win32/eventlog/reporting-an-event
// 参考: https://www.cnblogs.com/jqdy/p/14993286.html
// C:\dev-tools\vs2022\VC\Auxiliary\Build\vcvarsall.bat x64 10.0.22621.0
// mc log4winevent.mc
// rc log4winevent.rc
// link -dll -noentry log4winevent.res
#include "log4winevent.h"

// rolling_file 型 writer 数据结构(注意前面几个字段要与 _log_writer_t 的相同)
typedef struct _log_writer_syslog {
    void (*write)(struct _log_writer_syslog* self, const char* msg);
    void (*flush)(struct _log_writer_syslog* self);
    void (*close)(struct _log_writer_syslog* self);
    HANDLE hEventSource;
    char name[32];
} log_writer_syslog_t;

static inline void _syslog_write(log_writer_syslog_t* self, const char* msg) {
    // 根据日志字符串中的级别，设置事件级别
    // "EMERG ", "ALERT ", " CRIT ", "ERROR ", " WARN ", " NOTE ", " INFO ", "DEBUG "
    WORD wType = EVENTLOG_SUCCESS;
    /**/ if (memcmp(msg + 24, "DEBUG", 5) == 0) wType = EVENTLOG_INFORMATION_TYPE;
    else if (memcmp(msg + 24, "INFO",  4) == 0) wType = EVENTLOG_INFORMATION_TYPE;
    else if (memcmp(msg + 24, "WARN",  4) == 0) wType = EVENTLOG_WARNING_TYPE;
    else if (memcmp(msg + 24, "ERROR", 5) == 0) wType = EVENTLOG_ERROR_TYPE;
    else if (memcmp(msg + 24, "ALERT", 5) == 0) wType = EVENTLOG_ERROR_TYPE;
    else if (memcmp(msg + 24, "CRIT",  4) == 0) wType = EVENTLOG_ERROR_TYPE;
    else if (memcmp(msg + 24, "EMERG", 5) == 0) wType = EVENTLOG_ERROR_TYPE;
    else if (memcmp(msg + 24, "NOTE",  4) == 0) wType = EVENTLOG_INFORMATION_TYPE;

    LPCSTR lpStrings[] = {msg}; // 要写入日志的信息
    ReportEventA(
        self->hEventSource,// event log handle
        wType,             // event type
        LOG_CATEGORY,      // event category
        MSG_LOG_STR,       // event identifier
        NULL,              // no security identifier
        1,                 // size of lpszStrings array
        0,                 // no binary data
        lpStrings,         // array of strings
        NULL);             // no binary data
}

static inline void _syslog_close(log_writer_syslog_t* self) {
    DeregisterEventSource(self->hEventSource);
}

log_writer_t* logger_add_syslog(logger_t* logger, const char *name) {
    if(!logger || !name || !strlen(name)) return NULL;

    // 查找一个 writer 空位置
    int idx = _first_writer(logger, NULL); if(idx < 0) return NULL;

    // 通用命名约定 (UNC) 要对其执行此操作的远程服务器的名称。 如果此参数为 NULL，则使用本地计算机。
    // 宏 SVCNAME 定义了服务的名称。源名称必须是 Eventlog 注册表项下的日志的子项。
    HANDLE hEventSource = RegisterEventSourceA(NULL, name);
    if (NULL == hEventSource) {
        printf("RegisterEventSource() failed: %s", get_windows_error_msg());
        return NULL;
    }

    log_writer_syslog_t* writer = MALLOC0(log_writer_syslog_t);
    if(!writer) return NULL;
    writer->hEventSource = hEventSource;
    strncpy(writer->name, name, sizeof(name));
    
    logger->writers[idx] = (log_writer_t*)writer;
    return (log_writer_t*)writer;
}

#elif(__linux__ || __unix__)

// Syslog 相关实现
static inline void _syslog_write(log_writer_t* self, const char* msg) {
    // 根据日志字符串中的级别，设置事件级别
    // "EMERG ", "ALERT ", " CRIT ", "ERROR ", " WARN ", " NOTE ", " INFO ", "DEBUG "
    int level = 0;
    /**/ if (memcmp(msg + 24, "DEBUG", 5) == 0) level = LOG_DEBUG;
    else if (memcmp(msg + 24, "INFO",  4) == 0) level = LOG_INFO;
    else if (memcmp(msg + 24, "WARN",  4) == 0) level = LOG_WARNING;
    else if (memcmp(msg + 24, "ERROR", 5) == 0) level = LOG_ERR;
    else if (memcmp(msg + 24, "ALERT", 5) == 0) level = LOG_ALERT;
    else if (memcmp(msg + 24, "CRIT",  4) == 0) level = LOG_CRIT;
    else if (memcmp(msg + 24, "EMERG", 5) == 0) level = LOG_EMERG;
    else if (memcmp(msg + 24, "NOTE",  4) == 0) level = LOG_NOTICE;
    syslog(LOG_USER | level, "%s", msg);
}

log_writer_t* logger_add_syslog(logger_t* logger, const char *name) {
    return logger_add_writefunc(logger, _syslog_write, NULL);
}

#else

log_writer_t* logger_add_syslog(logger_t* logger, const char *name) {
    return NULL;
}

#endif

logger_t* logger_init(logger_t *logger) {    
    // 初始化日志数据结构
    logger->putc    = logger_putchar;
    logger->puts    = logger_puts;
    logger->printf  = logger_printf;
    logger->dumpbin = logger_dumpbin;
    logger->log     = logger_log;
    logger->flush   = logger_flush;
    logger->level   = LOG_DEBUG;
    ZEROARR(logger->writers);
    //unios_critical_section_init(&logger->cs);
    logger->cs = NULL;
    return logger;
}

logger_t* logger_create() {
    // 分配日志数据结构空间
    logger_t* logger = (logger_t*)unios_malloc(sizeof(logger_t));
    if (!logger) return NULL;
	
    // 初始化日志数据结构
    return logger_init(logger);
}

void logger_close(logger_t* logger) {
    if (!logger) return;
    //logger->log(logger, LOG_INFO, "LOGGER", "Logger closed.\n");
    logger->puts(logger, "Logger closed.\n");

    unios_critical_section_enter(logger->cs, 1000);
    for (int iw = 0; iw < LOG_MAX_WRITER; iw++) {
        if (logger->writers[iw] == NULL) continue;
        if (logger->writers[iw]->close != NULL) {
            logger->writers[iw]->close(logger->writers[iw]); // 如果 writer 有 close 函数指针，则调用它关闭 writer
        } else {
            unios_free(logger->writers[iw]);                       // 否则直接释放 writer 内存
        }
        logger->writers[iw] = NULL; // 清空 writer 登记表项
    }
    unios_critical_section_destroy(&logger->cs);
    unios_free(logger);        // 释放 logger 空间
}
